Skip to content

skills: one canonical skill dir, served over HTTP, fetched by the CLI - #12

Merged
sdelcore merged 1 commit into
mainfrom
serve-skill-from-repo
Aug 12, 2026
Merged

skills: one canonical skill dir, served over HTTP, fetched by the CLI#12
sdelcore merged 1 commit into
mainfrom
serve-skill-from-repo

Conversation

@sdelcore

Copy link
Copy Markdown
Owner

Why

The shared-sites skill had drifted into separate copies. One was embedded at internal/web/init/SKILL.md and shipped by the CLI; others were maintained by hand outside the repo. The embedded copy was also stale against the server: it documented neither streaming chat, nor ai.image, nor the per-site AI rate limit, all of which shared.js and the AI handlers already support. An agent reading it would not know those features exist, and would read a 429 as a bug.

This makes the repo the single source and the running server the distribution point.

What changed

  • One canonical location. The skill moves to skills/shared-sites/SKILL.md, beside install-shared-cli. A new top-level skills package embeds both. The embed lives there because go:embed cannot reference a parent directory.

  • The skill is current again. It now covers streaming chat, ai.image, the AI environment variables and rate limit, and the three silent-failure traps: db.subscribe takes a handlers object, ws.onMessage is a method, and ai.chat takes two positional arguments.

  • The server serves them.

    Endpoint Returns
    GET /skill.md the shared-sites skill (base host only)
    GET /api/skills the available skills and their URLs
    GET /api/skills/{name} one skill as text/markdown

    /skill.md is base-host only. On a site host that path belongs to the site, so a deployed file of the same name still wins. /shared.js is global by design; a skill file does not need to be.

  • shared skill install fetches from the server and falls back to the built-in copy when the server is unreachable. An agent then reads the skill for the server it deploys to, not the one the CLI was built from. The fetch requires the --- frontmatter, so an error page from a proxy or an older server falls back instead of writing garbage.

  • The README install instructions for install-shared-cli drop the git clone in favour of a curl from the server.

Testing

Against a local sharedd:

  • GET /skill.md on the base host returns the skill as text/markdown; the same path on mysite.localhost does not.
  • GET /api/skills lists both skills; GET /api/skills/nope returns 404, and a name with a path separator is rejected before it reaches the embedded FS.
  • shared skill install writes the fetched copy, skips an existing file, and reports (from built-in copy) on stderr when the server is refused.
  • shared init still scaffolds the skill.
  • go vet, go test, the three-OS cross-compile, and nix build all pass.

Not included

Rendering the skill per server (substituting the real base host into the URLs) is the natural follow-up. It would let a homelab drop its hand-maintained copy entirely. It is left out here to keep this change to one concern.

The shared-sites skill had drifted into separate copies: one embedded at
internal/web/init/SKILL.md and shipped by the CLI, and hand-maintained copies
elsewhere. The embedded copy was also stale against the server — it documented
neither streaming chat, nor ai.image, nor the per-site AI rate limit, all of
which the served shared.js and the AI handlers already support.

Make the repo the single source and the server the distribution point:

- Move the skill to skills/shared-sites/SKILL.md, beside install-shared-cli,
  and embed both through a new top-level skills package (go:embed cannot
  reach above its own package directory, so the embed lives there).
- Fold the missing API surface into the skill: streaming chat, ai.image, the
  AI env vars and rate limit, and the silent-failure notes for db.subscribe,
  ws.onMessage, and the positional args of ai.chat.
- Serve them: GET /skill.md on the base host, plus GET /api/skills and
  GET /api/skills/{name}. /skill.md is base-host only, so a deployed site file
  of the same name still wins, unlike /shared.js which is global.
- shared skill install now fetches from the server and falls back to the
  built-in copy when it is unreachable, so an agent reads the skill for the
  server it deploys to rather than the one the CLI was built from.
@sdelcore
sdelcore merged commit e2ca62f into main Aug 12, 2026
2 checks passed
@sdelcore
sdelcore deleted the serve-skill-from-repo branch August 12, 2026 01:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant